﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using System.Data;
using Microsoft.SharePoint;
using System.Xml.Serialization;
using Microsoft.Office.Server.Search.Administration;
using Microsoft.Office.Server.Search.Query;
using System.Web.Configuration;
using System.IO;
using System.Xml;
using PS.WebParts.Rollup.Search.Core.Query;
using System.Web.UI;

namespace PS.WebParts.Rollup.Search.Core
{
    public class SearchInstruction
    {
        public SearchInstruction()
        {
            MossSearch = new MOSSSQLSearch();
            MossSearch.SelectStatement = new SelectStatement();
            MossSearch.SelectStatement.ReturnProperties = new List<string>();

            MossSearch.Where = new WhereStatement();
            this.ResultTypes = new List<ResultType>();
            this.RowLimit = 100;

            MossSearch.Where.Scopes = new List<string>();

        }

        #region Properties
        public System.Int32? HighlightedSentenceCount { get; set; }

        public System.Boolean? EnableStemming { get; set; }

        public Microsoft.Office.Server.Search.Query.QueryHint? Hint { get; set; }

        public System.Boolean? IgnoreAllNoiseQuery { get; set; }

        public Microsoft.Office.Server.Search.Query.KeywordInclusion? KeywordInclusion { get; set; }

        public System.Int32? RowLimit { get; set; }

        public System.Int32? StartRow { get; set; }

        public System.Int32? Timeout { get; set; }

        public System.Int32? TotalRowsExactMinimum { get; set; }

        public System.Boolean? TrimDuplicates { get; set; }

        public List<ResultType> ResultTypes { get; set; }

        public string SiteContext { get; set; }

        public string SPSiteUrl { get; set; }

        public MOSSSQLSearch MossSearch { get; set; }
        #endregion

        [XmlIgnore]
        public DataTable SearchResult { get; set; }

        public void Run()
        {
            SearchResult = ExecuteOMSearch();

            List<string> columns = new List<string>(MossSearch.SelectStatement.ReturnProperties.ToArray());
            columns.AddRange(SelectStatement.GetDefaultProperties());

            //Changing the case of the row names (they seem to be upper case from search)
            columns.ForEach(a =>
                {
                    DataColumn foundColumn = null;

                    foreach (DataColumn column in SearchResult.Columns)
                    {
                        if (column.ColumnName.Equals(a, StringComparison.InvariantCultureIgnoreCase))
                        {
                            if (column.ColumnName != a) // Character casing is different
                                foundColumn = column;                                
                            
                            break;
                        }
                    }

                    if (foundColumn != null)
                        foundColumn.ColumnName = a;
                });
        }

        internal string GenerateSQL(SPWeb web)
        {
            return sql = MossSearch.ToString(web);
        }

        internal string sql = string.Empty;

        private DataTable ExecuteOMSearch()
        {
            DataTable table3 = new DataTable();

            //try
            {
                //using (SPSite site = new SPSite(SPSiteUrl))
                SPSite site = SPContext.Current.Site;
                {
                    using (Microsoft.Office.Server.Search.Query.FullTextSqlQuery query =
                        new Microsoft.Office.Server.Search.Query.FullTextSqlQuery((SearchContext.GetContext(site).ServerContext)))
                    {

                        query.QueryText = this.GenerateSQL(SPContext.Current.Web);

                        string missingAnd = ") ) (";

                        if (query.QueryText.Contains(missingAnd)) //bug in generator
                        {
                            query.QueryText = query.QueryText.Replace(missingAnd, ")) AND (");
                        }

                        //NotifyVerbose(string.Format("Query String: {0}", query.QueryText));

                        if (this.ResultTypes.Count == 0)
                            this.ResultTypes.Add(ResultType.RelevantResults);

                        this.ResultTypes.ForEach(a => query.ResultTypes |= a);

                        if (!String.IsNullOrEmpty(this.SiteContext))
                            query.SiteContext = new Uri(SiteContext);

                        if (this.RowLimit.HasValue)
                            query.RowLimit = this.RowLimit.Value;

                        if (this.StartRow.HasValue)
                            query.StartRow = this.StartRow.Value;

                        if (this.HighlightedSentenceCount.HasValue)
                            query.HighlightedSentenceCount = this.HighlightedSentenceCount.Value;

                        if (this.EnableStemming.HasValue)
                            query.EnableStemming = this.EnableStemming.Value;

                        if (this.Hint.HasValue)
                            query.Hint = this.Hint.Value;

                        if (this.IgnoreAllNoiseQuery.HasValue)
                            query.IgnoreAllNoiseQuery = this.IgnoreAllNoiseQuery.Value;

                        if (this.KeywordInclusion.HasValue)
                            query.KeywordInclusion = this.KeywordInclusion.Value;
                        if (this.Timeout.HasValue)
                            query.Timeout = this.Timeout.Value;
                        if (this.TrimDuplicates.HasValue)
                            query.TrimDuplicates = this.TrimDuplicates.Value;

                        query.AuthenticationType =
                                             SPSecurity.AuthenticationMode != AuthenticationMode.Windows
                                                 ? QueryAuthenticationType.PluggableAuthenticatedQuery
                                                 : QueryAuthenticationType.NtAuthenticatedQuery;

                        ResultTableCollection tables = null;

                        tables = query.Execute();

                        this.ResultTypes.ForEach(a =>
                        {
                            ResultTable reader = tables[a];
                            table3.Load(reader, LoadOption.OverwriteChanges);
                        });
                    }
                }
            }
            //catch (QueryMalformedException ex)
            //{
            //    //this.NotifyException(ex);
            //}
            //catch (Exception exception2)
            //{
            //    //this.NotifyException(exception2);
            //}
            return table3;
        }

        #region Serialization

        private static Type[] knownTypes = null;

        public static Type[] KnownTypes
        {
            get
            {
                if (knownTypes == null)
                {
                    List<Type> temp = new List<Type>();
                        
                    temp.Add(typeof(WherePropertyContains));
                    temp.Add(typeof(WhereMembershipContains));
                    
                    knownTypes = temp.ToArray();
                }

                return knownTypes;
            }
        }

        public string Serialize()
        {
            XmlDocument d = new XmlDocument();

            d.LoadXml("<d/>");

            System.Xml.Serialization.XmlSerializer x = new System.Xml.Serialization.XmlSerializer(this.GetType(), KnownTypes);

            string output = string.Empty;

            using (MemoryStream memoryStream = new MemoryStream())
            {
                x.Serialize(memoryStream, this);

                byte[] chars = memoryStream.ToArray();

                output = Encoding.UTF8.GetString(chars);
            }

            return output;
        }

        public static object Desearilize(Type type, string rawValue)
        {
            XmlSerializer serializer = new XmlSerializer(type, KnownTypes);

            XmlReader reader = System.Xml.XmlReader.Create(new System.IO.StringReader(rawValue));

            return serializer.Deserialize(reader);
        }

        #endregion
    }
}
